Remoting was introduced primarily to address
limitations in COM and DCOM. As with DCOM, it provided a distributed
computing mechanism that allowed clients to instantiate components on
remote computers. However, it allowed for the client and server
components to be configured using an XML configuration file,
essentially enabling .NET Remoting to perform remote communication by
sending binary messages and XML messages based on SOAP. It was designed
expressly for .NET-to-.NET communication requirements.
.NET Remoting Architecture
Similar to other distributed technologies, .NET Remoting uses a proxy object for communication (Figure 1).
The proxy class impersonates remote objects locally and abstracts out
the plumbing required for cross-process communication. In essence, the
proxy class intercepts calls and communicates with the remote object on
behalf of the client.
The package object
communicates with the formatter component that packages the client
request and server response in a suitable format. The formatter
serializes the message to SOAP or a binary format.
The formatter then
communicates with the transport channel that uses the appropriate
transport protocol to transmit the data. This multilayered .NET
Remoting architecture is extensible and allows new formatters and
channels to be introduced. The channel and formatter for an existing
.NET application can be changed in the application configuration file
without recompiling the code.
The ability to
configure a distributed application using a configuration file was very
effective. This practice was carried forward to ASP.NET Web services
and later to WCF.
.NET Remoting provided three primary groups of classes:
Serializable Classes
Serializable
objects could be copied from one process or application domain to
another. Application domains were the units of isolation for the common
language runtime. They were marked with the serializable attribute.
Serializable classes typically played the role of data contracts. The
serializable attribute was used to serialize a class into a byte stream
and marshal the byte stream across process boundaries where it was
reconstructed.
Most .NET types that
were part of the framework were serializable. This included basic data
types, such as integers, numbers, and dates, as well as complex
structures such as strings and datasets. Custom classes could be made
serializable by decorating them with [serializable].
Remotable Classes
Remotable classes were derived from System.MarshalByRefObject. It provided the class with the capability to be invoked remotely. Every public property and method in a Remotable class could be used remotely.
Ordinary Classes
Normal classes on their own could not be used in a .NET Remoting scenario, but were used in applications.
Hosting .NET Remoting Components
Remotable objects, created by deriving the class from System.MarshalByRefObject,
had to be hosted in an application domain for it to be accessed
remotely. .NET Remoting components could be hosted in several ways,
including the following.
Windows Service
A Windows service started
automatically when the server started and was simple to maintain.
However, it came with several disadvantages. .NET Remoting did not
include its own security model, and therefore the plumbing code for
authentication, authorization, and confidentiality needed to be
custom-developed. Building security required working with channels and
formatters, which was not a trivial task. Further, a Windows service
did not include high availability and scalability infrastructure.
Hosting a .NET component in a Windows service had limited applicability
and was primarily used in smaller implementations when security and
scalability were not important.
IIS Hosting Under ASP.NET
Remoting
server-side objects could be hosted in Internet Information Service
(IIS). This approach had several advantages, including inheriting the
security, auditing, and scalability infrastructure provided by IIS.
Security features supplied by IIS included authentication,
authorization, and confidentiality using secure communications. Also, a
.NET component hosted in IIS could be configured to use Windows
authentication. Forms authentication was not supported, as .NET
Remoting could not access cookies.
This design introduced
several challenges. The objects had to use the HTTP channel to be
hosted in IIS. The IIS worker process could recycle the ASP.NET worker
process and, as a result, .NET Remoting could only be used as a
single-call object. Other activation modes, including singleton and
client-activated objects (explained shortly), were not supported in IIS.
Hosting a .NET Remoting Component in a Console Application
.NET Remoting components
could also be hosted in a console application. In this case, the
console application had to remain running to accept remote calls.
Writing a host based on a console application was simple. However, this
approach was typically used for demos and testing. It was completely
unsuitable for a production environment, as it did not include security
and scalability infrastructure.
.NET COM+ Services
.NET Remoting objects
could be hosted in component services (COM+ services) and leveraged
various services, such as transactions, just-in-time (JIT), and object
pooling. Remoted classes had to inherit from MarshalByRefObject, and COM+ components inherited from ServicedComponent. ServicedComponent, which was derived from ContextBoundObject, was further derived from MarshalByRefObject.
Therefore, service components were remotable objects and it was
possible to have COM+ services encapsulate .NET Remoting classes.
In practice this
was extremely unintuitive to develop and configure, and was further
made difficult by the fact that Microsoft had not provided any real
guidance for using COM+ services with .NET Remoting.
.NET Remoting Configurations
.NET Remoting could be
used in different ways and configured for different purposes. For
example, the primary design choices offered by .NET Remoting included:
Each of these is explained in the following sections.
Activation Types
Remoting objects could
be classified as server-activated objects (SAO) or client-activated
objects (CAO). Server-activated objects were objects with lifetimes
controlled by the server. They were instantiated when a client invoked
a method for the first time. Server-activated objects were also
referred to as “well-known” objects because their location was always
known. Server-activated objects could be activated as SingleCall or Singleton.
SingleCall objects were stateless objects automatically created on the server when a client invoked a method (Figure 2).
These objects were only created for the duration of the method and each
call required a new instance of the object to be created. Because
SingleCall objects were stateless, they could easily be clustered by
hosting them on multiple machines.
A .NET Remoting object could be configured as a SingleCall object in the application configuration file, as shown here:
Example 1.
<service> <wellknown mode="SingleCall" type="Greet.Greet Service, Hello" objectUri="HelloService.soap" /> </service>
|
With Singleton, a sole instance of the object was created and all clients were served by that instance (Figure 3).
The Singleton object retained state between every client and it had to
be thread-safe as multiple clients could concurrently access object
methods.
A .NET Remoting object could be configured as a Singleton object in the application configuration file, as follows:
Example 2.
<service> <wellknown mode="Singleton" type="Greet.Greet Service, Hello" objectUri="HelloService.soap" /> </service>
|
With
client-activated objects, one instance of the remote object was created
on the server for each client at the request of the client. The
instance retained state between client operation calls and once
activated by a client it could not be used by other clients. The
client-activated objects were created on the server when the client
called new or Activator.CreateInstance().
The client controlled the lifetime of the remote object using the
lifetime leasing system. Also, client-activated objects did not need a
URL and the wellknown tag was replaced by the activated tag.
A .NET Remoting object could be configured as a client-activated object in the application configuration file, as shown here:
Example 3.
<service> <activated type="Hello.HelloService, Hello" objectUri="HelloService.soap" /> </service>
|
.NET Remoting provided two formatters (binary and SOAP) and two channels (TCP and HTTP).
Message Formats
The binary formatter
serialized data to a proprietary .NET format, whereas the SOAP
formatter serialized data to SOAP messages. These SOAP messages were
larger and less efficient than the binary data. While .NET Remoting
could generate SOAP messages, it did not create corresponding WSDL
definitions. Using SOAP as the formatter therefore did not directly
enable the use of Web services.
Communication Protocols
The TcpChannel option relied on the TCP protocol and was well suited for high performance internal applications. HttpChannel
used the HTTP protocol and was better for communication via the
Internet and broader deployments. A .NET Remoting component could be
hosted in IIS 5.0/6.0 only if it used HTTP for transport. Configuring a
channel also required a port number over which the data was transmitted.
The formatter is defined in the application configuration file. For example, here we use the binary formatter over HTTP:
Example 4.
<channel ref="http" useDefaultCredentials="true" port="0"> <clientProviders> <formatter ref="binary"/> </clientProviders> </channel>
|
Object Lifetime Management
Singleton
and client-activated objects had state, while SingleCall objects were
stateless. After a stateful object was created on the server, it had to
be released after serving its purpose. Over time, unreleased objects
would use up valuable server resources that could end up “choking” the
server. In .NET Remoting, distributed garbage collection was addressed
using the lifetime leasing system, which was considered an improvement
over the keep-alive packets used in DCOM.
With the lifetime leasing
system, objects were allowed to live for a fixed length of time. If an
object was required for a longer period, another process had to renew
its lease. With a Singleton, the object lease was managed by the
server. The lease for a client-activated object was managed by the
client that created the remote object.
.NET Remoting permitted
objects to be marshaled between the client and the server either by
value or by reference. With Web services, objects were only passed by
value, and then serialized and reconstructed on the client.
.NET Remoting and Service-Orientation
.NET Remoting
introduced several innovative features that were eventually
incorporated into WCF. However, there were significant limitations when
attempting to apply service-orientation to .NET Remoting-based
solutions.
For example:
It was highly
proprietary and was therefore not designed to support open
interoperability among services. .NET Remoting objects could only be
used with other .NET clients and was primarily used as part of closed
distributed solutions that did not need to publish contracts or APIs.
It essentially was designed to accommodate .NET CLR-to-CLR
communication.
It
lacked the ability to share contracts as it does not include a
mechanism for interface discovery. Once the .NET Remoting server had
been developed, its interface had to be separated in an assembly and
provided directly to the service consumer (client), which then had to
generate a proxy from this assembly.
It
was not wire-compatible with ASMX and WCF even when configured to use
the SOAP formatter and HTTP as a transport. This was because .NET
Remoting relied on RPC/Encoded SOAP, whereas WCF and ASMX use the WS-I
Basic Profile (which requires DOC/Literal encoding).
It
was not easily ported to WCF. Migrating .NET Remoting code to WCF was,
for the most part, a rewrite of the service interface layer.
It inhibited the application of the Service Autonomy
principle. With .NET Remoting, the service consumer was tightly coupled
to the service and the assembly with the completed interface had to be
shipped to the service consumer so that it could be consumed. This
tight coupling resulted in a significant loss of autonomy potential.
Service-oriented design generally relies on a “contract first” approach in support of the Standardized Service Contract
principle. .NET Remoting was more geared toward bottom-up development.
Although Singletons and CAOs could not directly be used to design the
service contract, they could play a key role in implementing the logic
behind the service.
It has several hosting limitations and did not offer a security model.